<?php

namespace app\common\library;

use Exception;

/**
 * 个推 RestAPI v2 封装
 */
class Getui
{
    protected $config = [
        'app_id'     => 'zY7JjHVCNH69U2ACahwPj7',
        'app_key'    => 'dFwTNXEjHB93bAk1osrxz',
        'app_secret' => 'RUhJ5mCBHA7vO3SESTYal2',
        'master_secret' => 'NZzUo6TkK69PVzxiISBdU6',
        'api_host'   => 'https://restapi.getui.com/v2',
    ];

    protected $token = null;
    protected $tokenExpire = 0;

    public function __construct(array $config = [])
    {
        $siteConfig = config('getui') ?: [];
        $this->config = array_merge($this->config, $siteConfig, $config);
        if (!$this->config['app_id'] || !$this->config['app_key'] || !$this->config['master_secret']) {
            throw new Exception('个推配置不完整');
        }
    }

    /**
     * 获取 access_token
     * @throws Exception
     */
    protected function getToken(): string
    {
        if ($this->token && $this->tokenExpire > time() + 60) {
            return $this->token;
        }
        $url = $this->config['api_host'] . "/{$this->config['app_id']}/auth";
        // 时间戳必须是毫秒级，转为字符串
        $timestamp = (string)round(microtime(true) * 1000);
        // 签名算法：sha256(appkey + timestamp + mastersecret)
        $sign = hash('sha256', $this->config['app_key'] . $timestamp . $this->config['master_secret']);
        $payload = [
            'sign'      => $sign,
            'timestamp' => $timestamp, // payload中也使用字符串
            'appkey'    => $this->config['app_key'],
        ];
        $response = $this->request($url, $payload);
        if (empty($response['data']['token'])) {
            throw new Exception('获取个推token失败: ' . json_encode($response, JSON_UNESCAPED_UNICODE));
        }
        $this->token = $response['data']['token'];
        $this->tokenExpire = time() + (int)($response['data']['expire_time'] ?? 3600);
        return $this->token;
    }

    /**
     * 单推
     */
    public function pushToSingle(array $params)
    {
        $token = $this->getToken();
        $url = $this->config['api_host'] . "/{$this->config['app_id']}/push/single/cid";
        return $this->request($url, $params, [
            'token' => $token,
        ]);
    }

    /**
     * 群推（推送给指定用户列表）
     */
    public function pushToList(array $params)
    {
        $token = $this->getToken();
        $url = $this->config['api_host'] . "/{$this->config['app_id']}/push/list";
        return $this->request($url, $params, [
            'token' => $token,
        ]);
    }

    /**
     * 全量推送（推送给应用所有用户）
     * @param array $params 推送参数，包含：
     *   - request_id: 必填，10-32位的请求ID
     *   - group_name: 可选，任务组名
     *   - settings: 可选，推送设置（如ttl等）
     *   - push_message: 必填，推送消息内容
     *   - push_channel: 可选，厂商通道配置
     * @return array
     * @throws Exception
     */
    public function pushToApp(array $params)
    {
        $token = $this->getToken();
        $url = $this->config['api_host'] . "/{$this->config['app_id']}/push/all";
        // 确保audience为"all"
        $params['audience'] = 'all';
        // 如果没有设置request_id，自动生成
        if (empty($params['request_id'])) {
            $params['request_id'] = uniqid('gt_', true);
        }
        return $this->request($url, $params, [
            'token' => $token,
        ]);
    }

    /**
     * 停止任务
     */
    public function stopTask(string $taskId)
    {
        $token = $this->getToken();
        $url = $this->config['api_host'] . "/{$this->config['app_id']}/task/stop/$taskId";
        return $this->request($url, [], [
            'token' => $token,
        ], 'DELETE');
    }

    /**
     * 获取推送结果（不含自定义事件）
     * @param string|array $taskIds 任务ID，可以是单个字符串或多个ID的数组，多个ID会自动用逗号连接
     * @param bool $needGetuiByBrand 是否需要个推品牌报表，默认false
     * @return array
     * @throws Exception
     */
    public function getPushReport($taskIds, bool $needGetuiByBrand = false)
    {
        $token = $this->getToken();
        // 处理taskIds，支持字符串或数组
        if (is_array($taskIds)) {
            $taskIdStr = implode(',', $taskIds);
        } else {
            $taskIdStr = $taskIds;
        }
        // 验证taskIds数量（最多200个）
        $taskIdArray = explode(',', $taskIdStr);
        if (count($taskIdArray) > 200) {
            throw new Exception('taskId数量不能超过200个');
        }
        $url = $this->config['api_host'] . "/{$this->config['app_id']}/report/push/task/{$taskIdStr}";
        // 构建查询参数
        $queryParams = [];
        if ($needGetuiByBrand) {
            $queryParams['needGetuiByBrand'] = 'true';
        }
        if (!empty($queryParams)) {
            $url .= '?' . http_build_query($queryParams);
        }
        return $this->request($url, [], [
            'token' => $token,
        ], 'GET');
    }

    /**
     * 获取推送结果（含自定义事件）
     * @param string|array $taskIds 任务ID，可以是单个字符串或多个ID的数组，多个ID会自动用逗号连接
     * @param string|array $actionIdList 自定义事件ID列表，可以是单个字符串或多个ID的数组
     * @param bool $needGetuiByBrand 是否需要个推品牌报表，默认false
     * @return array
     * @throws Exception
     */
    public function getPushReportWithAction($taskIds, $actionIdList = null, bool $needGetuiByBrand = false)
    {
        $token = $this->getToken();
        // 处理taskIds，支持字符串或数组
        if (is_array($taskIds)) {
            $taskIdStr = implode(',', $taskIds);
        } else {
            $taskIdStr = $taskIds;
        }
        // 验证taskIds数量（最多200个）
        $taskIdArray = explode(',', $taskIdStr);
        if (count($taskIdArray) > 200) {
            throw new Exception('taskId数量不能超过200个');
        }
        $url = $this->config['api_host'] . "/{$this->config['app_id']}/report/push/task/{$taskIdStr}";
        // 构建查询参数
        $queryParams = [];
        if ($actionIdList !== null) {
            // 处理actionIdList，支持字符串或数组
            if (is_array($actionIdList)) {
                $actionIdStr = implode(',', $actionIdList);
            } else {
                $actionIdStr = $actionIdList;
            }
            $queryParams['actionIdList'] = $actionIdStr;
        }
        if ($needGetuiByBrand) {
            $queryParams['needGetuiByBrand'] = 'true';
        }
        if (!empty($queryParams)) {
            $url .= '?' . http_build_query($queryParams);
        }
        return $this->request($url, [], [
            'token' => $token,
        ], 'GET');
    }

    /**
     * 查询用户基础信息
     * @param string|array $cids 用户标识（cid），可以是单个字符串或多个ID的数组，多个ID会自动用逗号连接
     * @return array
     * @throws Exception
     */
    public function getUserBaseInfo($cids)
    {
        $token = $this->getToken();
        // 处理cids，支持字符串或数组
        if (is_array($cids)) {
            $cidStr = implode(',', $cids);
        } else {
            $cidStr = $cids;
        }
        // 验证cids数量（最多200个）
        $cidArray = explode(',', $cidStr);
        if (count($cidArray) > 200) {
            throw new Exception('cid数量不能超过200个');
        }
        $url = $this->config['api_host'] . "/{$this->config['app_id']}/user/info/base/{$cidStr}";
        return $this->request($url, [], [
            'token' => $token,
        ], 'GET');
    }

    /**
     * 公共请求方法
     */
    protected function request(string $url, array $data = [], array $headers = [], string $method = 'POST')
    {
        $ch = curl_init();
        $headerArr = [];
        // GET请求不需要Content-Type
        if ($method !== 'GET') {
            $headerArr[] = 'Content-Type: application/json';
        }
        foreach ($headers as $key => $value) {
            $headerArr[] = $key . ': ' . $value;
        }
        $options = [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HTTPHEADER => $headerArr,
            CURLOPT_TIMEOUT => 10,
        ];
        if ($method === 'POST') {
            $options[CURLOPT_POST] = true;
            $options[CURLOPT_POSTFIELDS] = json_encode($data, JSON_UNESCAPED_UNICODE);
        } elseif ($method === 'DELETE') {
            $options[CURLOPT_CUSTOMREQUEST] = 'DELETE';
        } elseif ($method === 'GET') {
            $options[CURLOPT_HTTPGET] = true;
        }
        curl_setopt_array($ch, $options);
        $result = curl_exec($ch);
        if ($result === false) {
            $error = curl_error($ch);
            curl_close($ch);
            throw new Exception('个推请求失败: ' . $error);
        }
        curl_close($ch);
        $json = json_decode($result, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new Exception('个推返回数据异常: ' . $result);
        }
        if (($json['code'] ?? '') !== 0 && ($json['code'] ?? '') !== '0') {
            throw new Exception('个推接口错误: ' . json_encode($json, JSON_UNESCAPED_UNICODE));
        }
        return $json;
    }
}

